
#include <windows.h>

#include "EbTftpSimuClnt.h"

UINT16 EbootCheckUDP( EDBG_ADDR *pMyAddr, 
                      BYTE      *pFrameBuffer, 
                      UINT16    *wDestPort, 
                      UINT16    *wSrcPort, 
                      UINT16   **pwData, 
                      UINT16    *cwLength );


BOOL CEbootSimuBin::Init(DWORD dwImgStart, DWORD dwImgLen, DWORD dwRecNumb)
{
	m_dwImgStart=dwImgStart;
	m_dwImgLen=dwImgLen;
	m_dwRecSize=dwImgLen/dwRecNumb;
	m_dwFileSize=7+4+4+dwRecNumb*(4+4+4+m_dwRecSize);	//sig+img_bgn+img_Len+256(rec_start+rec_len+chk+rec_size)
	m_dwCurRecPos=0;
	m_dwCurHdPos=0;
	m_dwCurFilePos=0;

	m_dwRecInfo[0]=m_dwImgStart;
	m_dwRecInfo[1]=m_dwRecSize;
	m_dwRecInfo[2]=CheckSum_FF(m_dwRecSize);

	m_dwLastPackageLen=0;

	return TRUE;
};

BOOL CEbootSimuBin::Read(BYTE* pbBuf, WORD* pwLen)
{
	DWORD dwCpLen;
	WORD wLen=*pwLen;
	m_dwLastPackageLen=0;
	do
	{
		dwCpLen=0;

		if(m_dwCurFilePos<7)
		{
			dwCpLen=min( (7-m_dwCurFilePos), wLen);
			memcpy(pbBuf, "B000FF\x0A"+m_dwCurFilePos, dwCpLen);
		}
		else if( m_dwCurFilePos<(7+4) )
		{
			dwCpLen=min( (7+4-m_dwCurFilePos), wLen);
			memcpy(pbBuf, (BYTE*)(&m_dwImgStart)+m_dwCurFilePos-7, dwCpLen);
		}
		else if( m_dwCurFilePos<(7+4+4) )
		{
			dwCpLen=min( (7+4+4-m_dwCurFilePos), wLen);
			memcpy(pbBuf, (BYTE*)(&m_dwImgLen)+m_dwCurFilePos-(7+4), dwCpLen);
		}
		else if(m_dwCurHdPos<sizeof(m_dwRecInfo))
		{
			dwCpLen=min( (sizeof(m_dwRecInfo)-m_dwCurHdPos), wLen);
			memcpy(pbBuf, (BYTE*)(&m_dwRecInfo)+m_dwCurHdPos, dwCpLen);

			m_dwCurHdPos+=dwCpLen;
		}
		else if(m_dwCurRecPos<m_dwRecInfo[1])
		{
			dwCpLen=min( (m_dwRecInfo[1]-m_dwCurRecPos), wLen);
			memset(pbBuf, 0xff, dwCpLen);

			m_dwCurRecPos+=dwCpLen;

			// next recorder
			if(m_dwCurRecPos==m_dwRecInfo[1])
			{
				m_dwCurHdPos=0;
				m_dwCurRecPos=0;
				m_dwRecInfo[0]+=m_dwRecSize;

				// the last valid record
				if((m_dwFileSize-m_dwCurFilePos-m_dwRecInfo[1])<(m_dwRecSize+12))
				{
					memset(m_dwRecInfo, 0, sizeof(m_dwRecInfo));
				}
			}
		}

		m_dwCurFilePos+=dwCpLen;
		wLen-=dwCpLen;
		pbBuf+=dwCpLen;
		m_dwLastPackageLen+=dwCpLen;
	}while( (wLen>0) 
			&& (m_dwCurFilePos<m_dwFileSize) 
			&& (0!=dwCpLen));

	*pwLen=m_dwLastPackageLen;

	return TRUE;
}


DWORD CEbootSimuBin::CheckSum_FF(DWORD dwLen)
{
	return DWORD(0xff*dwLen);
}


BOOL CEbootMemBin::Init(DWORD dwMemStart, DWORD dwMemLen)
{
	m_pMemStart=(BYTE*)dwMemStart;
	m_dwMemLen=dwMemLen;
	m_dwCurPos=0;

	return TRUE;
}

BOOL CEbootMemBin::Read(BYTE* pbBuf, WORD* pwLen)
{
	WORD wCpLen=min(*pwLen, m_dwMemLen-m_dwCurPos);

	memcpy(pbBuf, m_pMemStart+m_dwCurPos, wCpLen);

	m_dwCurPos+=wCpLen;
	*pwLen=wCpLen;

	return wCpLen?TRUE:FALSE;
}


BOOL CEbTftpSimuClnt::Init(IEbootSimuBin* pFile, char* pszName)
{
	m_pFile=pFile;
	strcpy(m_szFileName, pszName);

	m_State=STAT_WRQ;

	return TRUE;
}

BOOL CEbTftpSimuClnt::EbootGetData(BYTE* pbuf, WORD* pwLen)
{
	WORD wLen;
	// write request
	if(STAT_WRQ==m_State)
	{
		return MakeWRQPckt( pbuf, pwLen);
	}
	else if(STAT_WT_DATA==m_State)
	{
		WORD wDataLen, wHdLen=*pwLen;
		if(!MakeWtDataHd(pbuf, &wHdLen))
		{
			EdbgOutputDebugString("MakeWtDataHd faild\r\n");
			return FALSE;
		}
		pbuf+=wHdLen;
		
		wDataLen=*pwLen-wHdLen;
		if(!m_pFile->Read(pbuf, &wDataLen))
		{
			EdbgOutputDebugString("Read faild\r\n");
			return FALSE;
		}

		*pwLen=wHdLen+wDataLen;
		return TRUE;
	}
	else
	{
		EdbgOutputDebugString("invalid tftp state m_State:%d\r\n", m_State);
	}

	return FALSE;
}

BOOL CEbTftpSimuClnt::EbootSendData(BYTE* pbuf, DWORD dwLen)
{
	if( dwLen < TFTP_PCKT_MIN_LEN )
	{
		return FALSE;
	}

	BOOL fRet=FALSE;

	WORD wTftpOp, wBlkNum;
	GetOpCodeBlkNum(pbuf, &wTftpOp, &wBlkNum);
	if( (STAT_WRQ==m_State)
		&& (OP_ACK==wTftpOp)
		&& (0==wBlkNum) )
	{
//EdbgOutputDebugString("STAT_WRQ get ack\r\n");
			m_State=STAT_WT_DATA;
			m_wBlkNum=1;
			fRet=TRUE;
	}
	else if( (STAT_WT_DATA==m_State)
				&&(OP_ACK==wTftpOp)
				&&( m_wBlkNum == wBlkNum) )
	{
//EdbgOutputDebugString("STAT_WT_DATA get ack wBlkNum:0x%x, m_dwCurFilePos:0x%x\r\n", wBlkNum);
		fRet=TRUE;
		m_wBlkNum++;
	}
	else
	{
		EdbgOutputDebugString("invalid tftp state m_State:%d, wTftpOp:%d, m_wBlkNum:0x%x, wBlkNum:0x%x\r\n", m_State, wTftpOp, m_wBlkNum, wBlkNum);
	}

	return fRet;
}

BOOL CEbTftpSimuClnt::MakeWRQPckt(BYTE* pbuf, WORD* pwLen)
{
	if(*pwLen<(sizeof(WORD)+strlen(m_szFileName)+1+sizeof("octet")))
	{
		return FALSE;
	}
	*pwLen=sizeof(WORD)+strlen(m_szFileName)+1+sizeof("octet");

	WORD wRq=htons(OP_WRQ);
	memcpy(pbuf, (&wRq), sizeof(wRq) );
	pbuf+=sizeof(wRq);

	memcpy(pbuf, m_szFileName, strlen(m_szFileName)+1 );
	pbuf+=(strlen(m_szFileName)+1);

	memcpy(pbuf, "octet", sizeof("octet"));
	pbuf+=sizeof("octet");
}

BOOL CEbTftpSimuClnt::MakeWtDataHd(BYTE* pbuf, WORD* pwLen)
{
	if(*pwLen<(sizeof(WORD)+sizeof(WORD)))
	{
		return FALSE;
	}

	WORD wRq=htons(OP_DATA);
	memcpy(pbuf, (&wRq), sizeof(wRq) );
	pbuf+=sizeof(wRq);

	WORD wBlkNum=htons(m_wBlkNum);
//	WORD wBlkNum=m_wBlkNum;
	memcpy(pbuf, &wBlkNum, sizeof(wBlkNum) );
	pbuf+=sizeof(wBlkNum);

	*pwLen=sizeof(WORD)+sizeof(WORD);

	return TRUE;
}


VOID CEbTftpSimuClnt::GetOpCodeBlkNum( BYTE* pbuf, WORD* pwOp, WORD* pwBlkNum)
{
	*pwOp = ntohs(*((WORD *)pbuf));
	*pwBlkNum=ntohs(*(((WORD *)pbuf) + 1));
}




extern "C" PFN_EDBG_SEND_FRAME       pfnEDbgSendFrame;

UINT16 EbSimuTransEther_SndFram_Stub(BYTE *pbData, DWORD dwLength)
{
	return 0;
}

BOOL CEbSimuTransEther::Init(EDBG_ADDR* pMyAddr, EDBG_ADDR* pDesAddr)
{
	m_destAddr=*pDesAddr;
	m_myAddr=*pMyAddr;

	return TRUE;
}

#define FRAMETYPE(x)    ntohs(*(USHORT *)((x) + 6*sizeof(USHORT)))
BOOL CEbSimuTransEther::FormEthFrame(BYTE* pbufData, WORD wDataLen, BYTE* pbufEther, WORD* pwEtherLen )
{
	BOOL fRet=FALSE;

	// replace the org pfnEDbgSendFrame
	PFN_EDBG_SEND_FRAME pfnEthSndFram=pfnEDbgSendFrame;
	pfnEDbgSendFrame=EbSimuTransEther_SndFram_Stub;
	// call EbootSendUDP to form a ether udp fram

	if(EbootSendUDP(pbufEther, &m_destAddr, &m_myAddr, pbufData, wDataLen))
	{
//		*pwEtherLen=wDataLen+sizeof(EthernetFrameHeader)+sizeof(IPHeaderFormat)+sizeof(UDPHeaderFormat);
		*pwEtherLen=wDataLen+42;
		fRet=TRUE;
	}

	// restore pfnEDbgSendFrame
	pfnEDbgSendFrame=pfnEthSndFram;

	return fRet;
}

BOOL CEbSimuTransEther::GetEtherPayload(BYTE* pbufEther, WORD wEtherLen, BYTE** ppDatabuf, DWORD* pdwDataLen)
{
	UINT16 wDestPort, wSrcPort;
 	if(!EbootCheckUDP(&m_myAddr, pbufEther, &wDestPort, &wSrcPort, (UINT16 **)ppDatabuf, (WORD*)pdwDataLen) )
	{
		// change the udp port 
if(m_destAddr.wPort!=wSrcPort)
{
EdbgOutputDebugString("m_destAddr.wPort:%d wSrcPort:%d\r\n", m_destAddr.wPort, wSrcPort);
}
		m_destAddr.wPort=(wSrcPort);
		m_myAddr.wPort=(wDestPort);

		return TRUE;
	}
}



